﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using XNAnimation;
using XNAnimation.Controllers;
using PloobsEngine.Utils;
using PloobsEngine.Publishers;
using PloobsEngine.Behaviors;

namespace PloobsEngine.Modelo.Animation
{
    public class AnimatedController : IAnimatedController
    {
        /// <summary>
        /// Serialization
        /// </summary>
        public AnimatedController()
        {
        }

        public  AnimatedController(IAnimatedModel model)
        {
            skinnedModel = model.GetAnimation() as SkinnedModel;            
            animationController = new AnimationController(skinnedModel.SkeletonBones);
            _obs = new ConcreteObserver(this);

        }
        
        public AnimatedController(IAnimatedModel model , String StartAnimationName)
            : this(model)
        {
            this.StartAnimationName = StartAnimationName;
            this.actualAnimation = StartAnimationName;
            animationController.StartClip(skinnedModel.AnimationClips[StartAnimationName]);        
            
        }

        public AnimatedController(IAnimatedModel model, String StartAnimationName,bool changeOnlyWhenDifferentAnimation)
            : this(model)
        {
            this.StartAnimationName = StartAnimationName;
            this.actualAnimation = StartAnimationName;
            animationController.StartClip(skinnedModel.AnimationClips[StartAnimationName]);
            this.changeOnlyWhenDifferentAnimation = changeOnlyWhenDifferentAnimation;

        }

        private bool changeOnlyWhenDifferentAnimation = true;
        private string actualAnimation;
        private SkinnedModel skinnedModel;        
        private AnimationController animationController;
        private String StartAnimationName = null;
        private IDictionary<String, String> _actionAnimation = new Dictionary<string, String>();
        private double transitionBetweenAnimationTimeInSeconds = 1;

        public double TransitionBetweenAnimationTimeInSeconds
        {
            get { return transitionBetweenAnimationTimeInSeconds; }
            set { transitionBetweenAnimationTimeInSeconds = value; }
        }

        public float Speed
        {
            get
            {
                return animationController.Speed;
            }
            set
            {
                this.animationController.Speed = value;
            }
        }

        public bool isLoop
        {
            get
            {
                return animationController.LoopEnabled;
            }
            set
            {
                this.animationController.LoopEnabled = value;
            }
        }

        public void MappActionAnimation(String actionName, String animation)
        {
            _actionAnimation.Add(actionName, animation);
        }


        #region IAnimatedController Members

        public void TransformBone(String boneName,Quaternion rot)
        {
            animationController.SetBoneController(boneName, rot);
        }

        public Matrix GetBoneAbsoluteTransform(String boneName)
        {
            return animationController.GetBoneAbsoluteTransform(boneName);
        }

        public void ChangeAnimation(string animationName, AnimationChangeMode mode)
        {
            if (changeOnlyWhenDifferentAnimation)
            {
                if (animationName != actualAnimation)
                {
                    animationController.CrossFade(
                            skinnedModel.AnimationClips[animationName],
                            TimeSpan.FromSeconds(transitionBetweenAnimationTimeInSeconds));
                    actualAnimation = animationName;
                }
            }
            else
            {
                animationController.CrossFade(
                     skinnedModel.AnimationClips[animationName],
                     TimeSpan.FromSeconds(transitionBetweenAnimationTimeInSeconds));
                actualAnimation = animationName;

            }            
        }        

        #endregion

        

        #region IAnimatedController Members


        public void Update(Microsoft.Xna.Framework.GameTime gt)
        {
            animationController.Update(gt.ElapsedGameTime, Matrix.Identity);
        }

        #endregion

        #region IObserver Members

        public void AddSubject(ISubject sub)
        {
            _obs.AddSubject(sub);
        }

        public void RemoveSubject(ISubject sub)
        {
            _obs.RemoveSubject(sub);
        }

        /// <summary>
        /// Implementacao do Update, recebera a notificacao do Subject e a tratara
        /// </summary>
        /// <param name="obj"></param>
        public void Recieve(object obj)
        {
            ActionPublishInformation api = obj as  ActionPublishInformation;
            String s = null;
            _actionAnimation.TryGetValue(api.AditionalInfo as string, out s);
            if(s != null)
            ChangeAnimation(s, AnimationChangeMode.Normal);
        }

        public void DetachFromAllSubjects()
        {
            _obs.DetachFromAllSubjects();
        }

        private IObserver _obs;
        #endregion

        #region IAnimatedController Members

        public void ChangeInterpolationMode(AnimationInterpolationMode im)
        {
            if (im == AnimationInterpolationMode.No_Interpolation)
            {

                animationController.TranslationInterpolation = InterpolationMode.None;
                animationController.OrientationInterpolation = InterpolationMode.None;
                animationController.ScaleInterpolation = InterpolationMode.None;
            }
            else if (im == AnimationInterpolationMode.Linear_Interpolation)
            {

                animationController.TranslationInterpolation = InterpolationMode.Linear;
                animationController.OrientationInterpolation = InterpolationMode.Linear;
                animationController.ScaleInterpolation = InterpolationMode.Linear;
            }
            else if (im == AnimationInterpolationMode.Cubic_Interpolation)
            {

                animationController.TranslationInterpolation = InterpolationMode.Cubic;
                animationController.OrientationInterpolation = InterpolationMode.Linear;
                animationController.ScaleInterpolation = InterpolationMode.Cubic;
            }
            else if (im == AnimationInterpolationMode.Spherical_Interpolation)
            {
                animationController.TranslationInterpolation = InterpolationMode.Linear;
                animationController.OrientationInterpolation = InterpolationMode.Spherical;
                animationController.ScaleInterpolation = InterpolationMode.Linear;

            }
        }

        #endregion

        #region IAnimatedController Members


        public Matrix[] GetBoneTransformations()
        {
            return animationController.SkinnedBoneTransforms;
        }
        public void Serialize(System.Xml.XmlTextWriter textWriter)
        {
            SerializerHelper.SerializeBaseType<bool>(changeOnlyWhenDifferentAnimation,"ChangeOnlyWhenDifferentAnimation",textWriter);
            SerializerHelper.SerializeBaseType<string>(actualAnimation, "ActualAnimation", textWriter);
            SerializerHelper.SerializeDictionary<string, string>(_actionAnimation, "ActionAnimation", textWriter);            
        
        }
       
        public void DeSerialize(System.Xml.XmlNode node, EngineStuff engine, IAnimatedModel model)
        {
            _actionAnimation = SerializerHelper.DeSerializeDictionary<string, string>("ActionAnimation", node);
            actualAnimation = SerializerHelper.DeserializeBaseType<String>("ActualAnimation", node);
            changeOnlyWhenDifferentAnimation = SerializerHelper.DeserializeBaseType<bool>("ChangeOnlyWhenDifferentAnimation", node);

            skinnedModel = model.GetAnimation() as SkinnedModel;
            animationController = new AnimationController(skinnedModel.SkeletonBones);
            _obs = new ConcreteObserver(this);

            animationController.StartClip(skinnedModel.AnimationClips[actualAnimation]);
        }

        #endregion
    }
}
